package followMe;

import java.awt.*;

import javax.sound.sampled.*;
import javax.swing.*;
import java.io.*;
//import javax.media.player;

import mote.Mote;
import mote.MoteBridge;
import mote.MoteCollection;

/**
 * FollowMeMusic class contains code for the game of FollowMeMusic.
 * @author Khalid Alhokail, Vineet Gupta, Oren Melzer, David Schneider
 */
public class FollowMeMusic {

	static final int MAX_SCALE = 2;
	static final int GAMEWIDTH = 800;
	static final int GAMEHEIGHT = 500;
	static final String GAMENAME = "Rave Revolution!";
	static final int NUM_READINGS = 30*20;
	static final int SLEEP_TIME = 20;
	static final double ACCURACY = .1;
	
	JFrame frame;
	
	Clip clip;
	
	public static void main(String args[])
	{
		new FollowMeMusic();
	}
	
	/**
	 * Starts playing music from a file containing song which gets playes while playing.
	 */
	public void PlayMusic()
	{
		System.out.println("Playing music");
		try{
			// From file
	        AudioInputStream stream = AudioSystem.getAudioInputStream(new File("/home/dave/workspace/cse520s-java/followMe/BLUE.wav"));
	    
	        AudioFormat format = stream.getFormat();
	        
	        // Create the clip
	        DataLine.Info info = new DataLine.Info(
	            Clip.class, stream.getFormat(), ((int)stream.getFrameLength()*format.getFrameSize()));
	        clip = (Clip) AudioSystem.getLine(info);
	    
	        // This method does not return until the audio file is completely loaded
	        clip.open(stream);
	    
	        // Start playing
        
		} catch (Exception e)
		{
			System.out.println(e);
		}
	}
	
	private void PlayClip()
	{
		clip.start();
	}
	
	/**
	 * This does the necessary intialization and sets a pattern
	 * to be followed by following players.
	 */
	public FollowMeMusic()
	{
		PlayMusic();
		FollowComponent fc = new FollowComponent();
		frame = new JFrame(GAMENAME);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.add(fc);
		frame.setSize(GAMEWIDTH, GAMEHEIGHT);
		frame.setVisible(true);
		
		
		MoteCollection mc = new MoteCollection();
		MoteBridge mb = new MoteBridge(mc);
		try 
		{
			while(mc.Count() < 1)
			{
				Thread.sleep(100);
			}
			
			Mote m = mc.Motes().get(0);
			
			mb.setSamplingInterval(m.GetId(), 1);	
			
			Thread.sleep(100);
			
			followRun(fc, m);
			
		} catch(Exception e)
		{
			System.out.println(e);
			System.exit(1);
		}	
	}
	
	/**
	 * Based on the pattern set initially, players need to
	 * follow it. Players performance is evaluated based on
	 * scoring logic in FollowComponent class.
	 * 
	 * @param fc Main FollowComponent.
	 * @param m Mote player is using.
	 */
	public void followRun(FollowComponent fc, Mote m){
		
		try{
		
			Thread.sleep(1000);
			clip.start();
			for(int i = 0; i < NUM_READINGS; ++i)
			{
				Thread.sleep(SLEEP_TIME);
				fc.addReading((int)m.AverageReading(1), true);
				fc.repaint();
			}
			clip.stop();
			Thread.sleep(10000);
			
			clip.start();
			for(int i = 0; i < NUM_READINGS; ++i)
			{
				Thread.sleep(SLEEP_TIME);
				fc.addReading((int)m.AverageReading(1), false);
				fc.repaint();
			}
			clip.stop();
		}catch(InterruptedException e){
			System.out.println("Sleep Interrupted");
		}
	}	
	
	/**
	 * Main GUI component.
	 */
	public class FollowComponent extends JComponent
	{
		double[] leader;
		double[] follower;
		int leaderCount = 0;
		int followerCount = 0;
		int currentCount = 0;
		Graphics2D g2d;
		
		FollowComponent()
		{
			leader = new double[FollowMeMusic.NUM_READINGS];
			follower = new double[FollowMeMusic.NUM_READINGS];		
		}
		
		public void paint(Graphics g)
		{
			g2d = (Graphics2D) g;
			
			g2d.setPaint(Color.black);
			g2d.drawLine(10, 0, 10, GAMEHEIGHT - 10);
			g2d.drawLine(10, GAMEHEIGHT - 10, GAMEWIDTH - 10, GAMEHEIGHT - 10);
			
			graph(leader, leaderCount, Color.BLUE);
			graph(follower, followerCount, Color.RED);
			
			String score = new Integer(score()*100).toString();
			g2d.drawString(score, GAMEWIDTH - 100, 10);
		}
		
		private void graph(double[] vals, int count, Color col){
			if(count <= 1) { return; }
			
			Point prev = new Point(getX(0), getY(vals[0]));
			Point curr = new Point();
			
			g2d.setPaint(col);
			
			for(int i = 1; i < count; ++i)
			{
				curr = new Point(getX(i), getY(last5average(vals, i)));
				g2d.drawLine(prev.x, prev.y, curr.x, curr.y);
				prev = curr;
			}
		}
		
		private double last5average(double[] vals, int i)
		{
			if(i >= 4)
			{
				return (vals[i] + vals[i-1] + vals[i-2] + vals[i-3] + 
					vals[i-4])/5;
			}
			else
			{
				return vals[i];
			}
		}
		
		private int score()
		{
			g2d.setPaint(Color.black);
			int streak = 0;
			int score = 0;
			for(int i = 0; i < followerCount; ++i)
			{
				if(Math.abs(leader[i]-follower[i])/leader[i] < ACCURACY)
				{
					// match
					++streak;
					
					// yay integer division!
					score += (streak/10) + 1;
					
					g2d.drawLine(getX(i), getY(last5average(leader, i)), 
							getX(i), getY(last5average(follower,i)));
				}
				else
				{
					streak = 0;
				}
			}
			return score;
		}
		
		public int getX(double r)
		{
			return (int) (10 + (r/(double)FollowMeMusic.NUM_READINGS)*GAMEWIDTH);
		}
		
		public int getY(double r)
		{
			int y = (int) (10 + (r/FollowMeMusic.MAX_SCALE)*GAMEHEIGHT);
			//return Math.max(1000-y, 0);
			return (GAMEHEIGHT-y) > 0 ? (GAMEHEIGHT-y) : 0;
		}
		
		public void addReading(int r, boolean isLeader)
		{
			if(isLeader){
				leader[leaderCount++] = Math.log10(r)-1.;
			}
			else {
				follower[followerCount++] = Math.log10(r)-1.;
			}
		}
	}
}
